#include <CODEPORTS/codeports.h>
#include "codeports.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

void
SCR_Init(void)
{
	unsigned int i;

	if((video.descriptor = open("/dev/fb0", O_RDWR)) == -1)
	{
		perror("Error: Unable to open framebuffer device /dev/fb0");
		exit(1);
	}

	ioctl(video.descriptor, FBIOGET_FSCREENINFO, &video.mode.fixed);
	ioctl(video.descriptor, FBIOGET_VSCREENINFO, &video.mode.original);

	memcpy(&video.mode.current, &video.mode.original, sizeof(struct fb_var_screeninfo));
	printf("Screen Modes:\n");
	printf("\t%i: %dx%d, %dbpp\n", 0, video.mode.original.xres, video.mode.original.yres, video.mode.original.bits_per_pixel);
	for(i = 1; i < sizeof(screen_modes) / sizeof(VIDEO_MODE); i++)
		printf("\t%i: %dx%d, %dbpp\n", i, screen_modes[i].xres, screen_modes[i].yres, screen_modes[i].bits_per_pixel);
	printf("Screen Mode: ");
	scanf("%i", &video.mode.code);
	getchar();

	if(video.mode.code)
	{
		video.mode.current.xres = screen_modes[video.mode.code].xres;
		video.mode.current.yres = screen_modes[video.mode.code].yres;
		video.mode.current.bits_per_pixel = screen_modes[video.mode.code].bits_per_pixel;
		ioctl(video.descriptor, FBIOPUT_VSCREENINFO, &video.mode.current);
	}
	switch(video.mode.current.bits_per_pixel >> 3)
	{
		case 1:
		{
			SCR_SetPaletteRGB();
			SCR_SetPixelRGB = SCR_SetPixelRGB8;
			SCR_GetPixelRGB = SCR_GetPixelRGB8;
			SCR_SetBoxRGB = SCR_SetBoxRGB8;
			SCR_SetBox = SCR_SetBoxRGB8;
			BOX_SetPixelRGB = BOX_SetPixelRGB8;
			BOX_GetPixelRGB = BOX_GetPixelRGB8;
			break;
		}
		case 2:
		{
			SCR_SetPixelRGB = SCR_SetPixelRGB16;
			SCR_GetPixelRGB = SCR_GetPixelRGB16;
			SCR_SetBoxRGB = SCR_SetBoxRGB16;
			SCR_SetBox = SCR_SetBoxRGB16;
			BOX_SetPixelRGB = BOX_SetPixelRGB16;
			BOX_GetPixelRGB = BOX_GetPixelRGB16;
			break;
		}
		case 3:
		{
			SCR_SetPixelRGB = SCR_SetPixelRGB24;
			SCR_GetPixelRGB = SCR_GetPixelRGB24;
			SCR_SetBoxRGB = SCR_SetBoxRGB24;
			SCR_SetBox = SCR_SetBoxRGB24;
			BOX_SetPixelRGB = BOX_SetPixelRGB24;
			BOX_GetPixelRGB = BOX_GetPixelRGB24;
			break;
		}
		case 4:
		{
			SCR_SetPixelRGB = SCR_SetPixelRGB32;
			SCR_GetPixelRGB = SCR_GetPixelRGB32;
			SCR_SetBoxRGB = SCR_SetBoxRGB32;
			SCR_SetBox = SCR_SetBoxRGBA;
			BOX_SetPixelRGB = BOX_SetPixelRGB32;
			BOX_GetPixelRGB = BOX_GetPixelRGB32;
			break;
		}
	}
	video.screen = mmap(NULL, video.mode.fixed.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, video.descriptor, 0);

	SWP_Init();
}

void
SCR_Final(void)
{
	SWP_Final();

	munmap(video.screen, video.mode.fixed.smem_len);

	ioctl(video.descriptor, FBIOPUT_VSCREENINFO, &video.mode.original);
    	close(video.descriptor);
}

void
SCR_GetSize(int *width, int *height)
{
	*width = video.mode.current.xres;
	*height = video.mode.current.yres;
}

void
SCR_GetDepth(int *depth)
{
	*depth = video.mode.current.bits_per_pixel >> 3;
}

void
SCR_Clear(void)
{
	memset(video.screen, 0, video.mode.fixed.smem_len);
	/*
	unsigned int i, j;
	for(j = 0; j < video.mode.current.yres; j++)
	{
		for(i = 0; i < video.mode.current.xres; i++)
		{
			switch((video.mode.current.bits_per_pixel >> 3))
			{
				case 1:
				{
					SCR_SetPixelColor(i, j, 0);
					break;
				}
				case 2:
				{
					SCR_SetPixelRGB16(i, j, 0, 0, 0);
					break;
				}
				case 3:
				{
					SCR_SetPixelRGB24(i, j, 0, 0, 0);
					break;
				}
				case 4:
				{
					SCR_SetPixelRGBA(i, j, 0, 0, 0, 0);
					break;
				}
			}
		}
	}
	*/
}

void
SCR_Swap(void)
{
	memcpy(video.screen, video.swap, video.mode.fixed.smem_len);
	//usleep(10000);
}

void
SCR_SetPalette(unsigned char pal[3][256])
{
	unsigned short fbpal[3][256];
	unsigned char c;
	for(c = 0; c < 256; c++)
	{
		fbpal[0][c] = pal[0][c];
		fbpal[1][c] = pal[1][c];
		fbpal[2][c] = pal[2][c];
	}
	video.mode.palette.start = 0;
	video.mode.palette.len = 256;
	video.mode.palette.red = fbpal[0];
	video.mode.palette.green = fbpal[1];
	video.mode.palette.blue = fbpal[2];
	video.mode.palette.transp = NULL;
	ioctl(video.descriptor, FBIOPUTCMAP, &video.mode.palette);
}

void
SCR_SetPixelColor(int x, int y, unsigned char c)
{
	if(0 <= x && x < video.mode.current.xres && 0 <= y && y < video.mode.current.yres)
 		*(video.screen + y * video.mode.fixed.line_length + x) = c;
}

void
SCR_SetPixelRGB8(int x, int y, unsigned char r, unsigned char g, unsigned char b)
{
	if(0 <= x && x < video.mode.current.xres && 0 <= y && y < video.mode.current.yres)
		*(video.screen + y * video.mode.fixed.line_length + x) = (r >> 4) << 6 | (g >> 3) << 3 | b >> 3;
}

void
SCR_SetPixelRGB16(int x, int y, unsigned char r, unsigned char g, unsigned char b)
{
	if(0 <= x && x < video.mode.current.xres && 0 <= y && y < video.mode.current.yres)
		*(unsigned short *)(video.screen + (y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3))) = (r << 11) | (g << 5) | (b);

}

void
SCR_SetPixelRGB24(int x, int y, unsigned char r, unsigned char g, unsigned char b)
{
	if(0 <= x && x < video.mode.current.xres && 0 <= y && y < video.mode.current.yres)
	{
		*(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 0) = b;
		*(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 1) = g;
		*(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 2) = r;
	}
}

void
SCR_SetPixelRGB32(int x, int y, unsigned char r, unsigned char g, unsigned char b)
{
	if(0 <= x && x < video.mode.current.xres && 0 <= y && y < video.mode.current.yres)
	{
		*(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 0) = b;
		*(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 1) = g;
		*(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 2) = r;
	}
}

void
SCR_SetPixelRGBA(int x, int y, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
	if(0 <= x && x < video.mode.current.xres && 0 <= y && y < video.mode.current.yres)
	{
		*(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 0) = b;
		*(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 1) = g;
		*(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 2) = r;
		*(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 3) = a;
	}
}

void
SCR_GetPixelColor(int x, int y, unsigned char *c)
{
	if(0 <= x && x < video.mode.current.xres && 0 <= y && y < video.mode.current.yres)
 		*c = *(video.screen + y * video.mode.fixed.line_length + x);
}

void
SCR_GetPixelRGB8(int x, int y, unsigned char *r, unsigned char *g, unsigned char *b)
{
	if(0 <= x && x < video.mode.current.xres && 0 <= y && y < video.mode.current.yres)
	{
		unsigned char c = *(video.screen + y * video.mode.fixed.line_length + x);
		*r = ((c & 0xC0) >> 6) << 4;
		*g = ((c & 0x38) >> 3) << 3;
		*b = (c & 0x07) << 3;
	}
}

void
SCR_GetPixelRGB16(int x, int y, unsigned char *r, unsigned char *g, unsigned char *b)
{
	if(0 <= x && x < video.mode.current.xres && 0 <= y && y < video.mode.current.yres)
	{
		unsigned short p = *(unsigned short *)(video.screen + (y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3)));
		*r = ((p & 0xF800) >> 11) << 3;
		*g = ((p & 0x07E0) >> 5) << 2;
		*b = (p & 0x001F) << 3;
	}
}

void
SCR_GetPixelRGB24(int x, int y, unsigned char *r, unsigned char *g, unsigned char *b)
{
	if(0 <= x && x < video.mode.current.xres && 0 <= y && y < video.mode.current.yres)
	{
		*b = *(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 0);
		*g = *(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 1);
		*r = *(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 2);
	}
}

void
SCR_GetPixelRGB32(int x, int y, unsigned char *r, unsigned char *g, unsigned char *b)
{
	if(0 <= x && x < video.mode.current.xres && 0 <= y && y < video.mode.current.yres)
	{
		*b = *(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 0);
		*g = *(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 1);
		*r = *(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 2);
	}
}

void
SCR_GetPixelRGBA(int x, int y, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a)
{
	if(0 <= x && x < video.mode.current.xres && 0 <= y && y < video.mode.current.yres)
	{
		*b = *(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 0);
		*g = *(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 1);
		*r = *(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 2);
		*a = *(video.screen + y * video.mode.fixed.line_length + x * (video.mode.current.bits_per_pixel >> 3) + 3);
	}
}

/*
void
SCR_SetBoxColor(int x, int y, BOX *box)
{
	unsigned int i, j;
	unsigned char c;
	for(j = 0; j < box->info.height; j++)
	{
		for(i = 0; i < box->info.width; i++)
		{
			BOX_GetPixelColor(box, i, j, &c);
			SCR_SetPixelColor(x + i, y + j, c);
		}
	}
}

void
SCR_SetBoxRGB8(int x, int y, BOX *box)
{
	unsigned int i, j;
	unsigned char r, g, b;
	for(j = 0; j < box->info.height; j++)
	{
		for(i = 0; i < box->info.width; i++)
		{
			BOX_GetPixelRGB8(box, i, j, &r, &g, &b);
			SCR_SetPixelRGB8(x + i, y + j, r, g, b);
		}
	}
}

void
SCR_SetBoxRGB16(int x, int y, BOX *box)
{
	unsigned int i, j;
	unsigned char r, g, b;
	for(j = 0; j < box->info.height; j++)
	{
		for(i = 0; i < box->info.width; i++)
		{
			BOX_GetPixelRGB16(box, i, j, &r, &g, &b);
			SCR_SetPixelRGB16(x + i, y + j, r, g, b);
		}
	}
}

void
SCR_SetBoxRGB24(int x, int y, BOX *box)
{
	unsigned int i, j;
	unsigned char r, g, b;
	for(j = 0; j < box->info.height; j++)
	{
		for(i = 0; i < box->info.width; i++)
		{
			BOX_GetPixelRGB24(box, i, j, &r, &g, &b);
			SCR_SetPixelRGB24(x + i, y + j, r, g, b);
		}
	}
}

void
SCR_SetBoxRGB32(int x, int y, BOX *box)
{
	unsigned int i, j;
	unsigned char r, g, b;
	for(j = 0; j < box->info.height; j++)
	{
		for(i = 0; i < box->info.width; i++)
		{
			BOX_GetPixelRGB32(box, i, j, &r, &g, &b);
			SCR_SetPixelRGB32(x + i, y + j, r, g, b);
		}
	}
}

void
SCR_SetBoxRGBA(int x, int y, BOX *box)
{
	unsigned int i, j;
	unsigned char r, g, b, a;
	for(j = 0; j < box->info.height; j++)
	{
		for(i = 0; i < box->info.width; i++)
		{
			BOX_GetPixelRGBA(box, i, j, &r, &g, &b, &a);
			SCR_SetPixelRGBA(x + i, y + j, r, g, b, a);
		}
	}
}

void
SCR_SetImage(char *image)
{
	MagickWand *mw;
	unsigned char *buffer;
	unsigned int i, j;
	buffer = malloc(video.mode.current.xres * video.mode.current.yres * 4);
	mw = NewMagickWand();
	MagickReadImage(mw, image);
	if(video.mode.current.xres != MagickGetImageWidth(mw) || video.mode.current.yres != MagickGetImageHeight(mw))
		MagickScaleImage(mw, video.mode.current.xres, video.mode.current.yres);
	MagickExportImagePixels(mw, 0, 0, video.mode.current.xres, video.mode.current.yres, "RGBA", CharPixel, buffer);
	DestroyMagickWand(mw);
	for(j = 0; j < video.mode.current.yres; j++)
	{
		for(i = 0; i < video.mode.current.xres; i++)
		{
			switch((video.mode.current.bits_per_pixel >> 3))
			{
				case 1:
				{
					SCR_SetPixelColor(i, j, buffer[(j * video.mode.current.xres + i) * 4]);
					break;
				}
				case 2:
				{
					SCR_SetPixelRGB16(i, j, buffer[(j * video.mode.current.xres + i) * 4 + 0], buffer[(j * video.mode.current.xres + i) * 4 + 1] , buffer[(j * video.mode.current.xres + i) * 4 + 2]);
					break;
				}
				case 3:
				{
					SCR_SetPixelRGB24(i, j, buffer[(j * video.mode.current.xres + i) * 4 + 0], buffer[(j * video.mode.current.xres + i) * 4 + 1] , buffer[(j * video.mode.current.xres + i) * 4 + 2]);
					break;
				}
				case 4:
				{
					SCR_SetPixelRGBA(i, j, buffer[(j * video.mode.current.xres + i) * 4 + 0], buffer[(j * video.mode.current.xres + i) * 4 + 1] , buffer[(j * video.mode.current.xres + i) * 4 + 2], buffer[(j * video.mode.current.xres + i) * 4 + 3]);
					break;
				}
			}
		}
	}
	free(buffer);
}

void
SCR_GetImage(char *image)
{
	MagickWand *mw;
	unsigned char *buffer;
	unsigned int i, j;
	unsigned char c, r, g, b, a;
	buffer = malloc(video.mode.current.xres * video.mode.current.yres * 4);
	for(j = 0; j < video.mode.current.yres; j++)
	{
		for(i = 0; i < video.mode.current.xres; i++)
		{
			switch((video.mode.current.bits_per_pixel >> 3))
			{
				case 1:
				{
					SCR_GetPixelColor(i, j, &c);
					buffer[(j * video.mode.current.xres + i) * 4 + 0] = c;
					buffer[(j * video.mode.current.xres + i) * 4 + 1] = c;
					buffer[(j * video.mode.current.xres + i) * 4 + 2] = c;
					buffer[(j * video.mode.current.xres + i) * 4 + 3] = 0;
					break;
				}
				case 2:
				{
					SCR_GetPixelRGB16(i, j, &r, &g, &b);
					buffer[(j * video.mode.current.xres + i) * 4 + 0] = r;
					buffer[(j * video.mode.current.xres + i) * 4 + 1] = g;
					buffer[(j * video.mode.current.xres + i) * 4 + 2] = b;
					buffer[(j * video.mode.current.xres + i) * 4 + 3] = 0;
					break;
				}
				case 3:
				{
					SCR_GetPixelRGB24(i, j, &r, &g, &b);
					buffer[(j * video.mode.current.xres + i) * 4 + 0] = r;
					buffer[(j * video.mode.current.xres + i) * 4 + 1] = g;
					buffer[(j * video.mode.current.xres + i) * 4 + 2] = b;
					buffer[(j * video.mode.current.xres + i) * 4 + 3] = 0;
					break;
				}
				case 4:
				{
					SCR_GetPixelRGBA(i, j, &r, &g, &b, &a);
					buffer[(j * video.mode.current.xres + i) * 4 + 0] = r;
					buffer[(j * video.mode.current.xres + i) * 4 + 1] = g;
					buffer[(j * video.mode.current.xres + i) * 4 + 2] = b;
					buffer[(j * video.mode.current.xres + i) * 4 + 3] = a;
					break;
				}
			}
		}
	}
	mw = NewMagickWand();
	MagickConstituteImage(mw, video.mode.current.xres, video.mode.current.yres, "RGBO", CharPixel, buffer);
	free(buffer);
	MagickWriteImage(mw, image);
	DestroyMagickWand(mw);
}
*/
